package mynl

import (
	"fmt"
	"strings"
)

/////////////////////////////////////////////////////////////////////////
// 解释器

/**
 * 遍历AST，执行函数调用。
 */
type Intepretor struct {
	AstVisitor
	//存储变量值的区域
	Values map[string]interface{}
}

func NewIntepretor() *Intepretor {
	var Values = make(map[string]interface{})
	return &Intepretor{Values: Values}
}

func (selfx Intepretor) Visit(caller *IAstVisitor, node *IAstNode) interface{} {
	return (*node).Accept(caller)
}

//函数声明不做任何事情。
func (selfx Intepretor) visitFunctionDecl(caller *IAstVisitor, functionDecl *FunctionDecl) interface{} {
	return nil
}

/**
 * 运行函数调用。
 * 原理：根据函数定义，执行其函数体。
 * @param functionCall
 */
func (selfx Intepretor) visitFunctionCall(caller *IAstVisitor, functionCall *FunctionCall) interface{} {
	if strings.HasPrefix(functionCall.Name, "nl.") {
		if functionCall.Name == "nl.println" { //内置函数
			if len(functionCall.Parameters) > 0 {
				var node IAstNode = functionCall.Parameters[0]
				var retVal = selfx.Visit(caller, &node)
				switch v := retVal.(type) {
				case *LeftValue:
					if v.variable != nil {
						retVal = selfx.getVariableValue(v.variable.Name)
					}
				default:
				}

				fmt.Println(retVal)
			} else {
				fmt.Println()
			}
		} else {
			fmt.Println("undefined sys func:" + functionCall.Name)
		}
	} else {
		//自定义函数
		//找到函数定义，继续遍历函数体
		if functionCall.FuncDecl != nil {
			selfx.visitBlock(caller, &functionCall.FuncDecl.Body)
		}
	}

	return nil
}

/**
 * 变量声明
 * 如果存在变量初始化部分，要存下变量值。
 * @param functionDecl
 */
func (selfx Intepretor) visitVariableDecl(caller *IAstVisitor, variableDecl *VariableDecl) interface{} {
	if variableDecl.init != nil {
		var node IAstNode = *(variableDecl.init)
		var vt = selfx.Visit(caller, &node)

		switch v := vt.(type) {
		case *LeftValue:
			vt = selfx.getVariableValue(v.variable.Name)
		default:
		}

		selfx.setVariableValue(variableDecl.Name, vt)
		return vt
	}

	return nil
}

/**
 * 获取变量的值。
 * 这里给出的是左值。左值既可以赋值（写），又可以获取当前值（读）。
 * @param v
 */
func (selfx Intepretor) visitVariable(caller *IAstVisitor, v *Variable) interface{} {
	return NewLeftValue(*v)
}

func (selfx Intepretor) getVariableValue(varName string) interface{} {
	return selfx.Values[varName]
}

func (selfx Intepretor) setVariableValue(varName string, value interface{}) bool {
	selfx.Values[varName] = value
	return true
}

/**
 * 处理二元运算
 */
func (selfx Intepretor) visitBinaryOP(caller *IAstVisitor, bi *BinaryOP) interface{} {
	fmt.Println("visitBinary:", bi.op)
	var node1 IAstNode = bi.exp1
	var node2 IAstNode = bi.exp2
	var visit1 = selfx.Visit(caller, &node1)
	var visit2 = selfx.Visit(caller, &node2)

	var visit1left *LeftValue
	var visit2left *LeftValue

	var value1 interface{}
	var value2 interface{}

	switch v := visit1.(type) {
	case *LeftValue:
		visit1left = v
		value1 = selfx.getVariableValue(v.variable.Name).(int)
		fmt.Println("value of ", visit1left.variable.Name, " : ", value1)
	default:
		value1 = visit1
	}

	switch v := visit2.(type) {
	case *LeftValue:
		visit2left = v
		value2 = selfx.getVariableValue(v.variable.Name).(int)
		fmt.Println("value of ", visit2left.variable.Name, " : ", value2)
	default:
		value2 = visit2
	}

	switch bi.op {
	case "+", "-", "*", "/", "%":
		v1, ok1 := value1.(int)
		v2, ok2 := value2.(int)
		if ok1 && ok2 {
			return selfx.visitBinaryMath(v1, v2, bi.op)
		} else {
			if ok1 {
				return selfx.visitBinaryOPMathF64(float64(v1), value2.(float64), bi.op)
			} else if ok2 {
				return selfx.visitBinaryOPMathF64(value1.(float64), float64(v2), bi.op)
			} else {
				return selfx.visitBinaryOPMathF64(value1.(float64), value2.(float64), bi.op)
			}
		}
		//break
	case ">", ">=", "<", "<=":
		v1, ok1 := value1.(int)
		v2, ok2 := value2.(int)
		if ok1 && ok2 {
			return selfx.visitBinaryOPCopmareF64(float64(v1), float64(v2), bi.op)
		} else {
			if ok1 {
				return selfx.visitBinaryOPCopmareF64(float64(v1), value2.(float64), bi.op)
			} else if ok2 {
				return selfx.visitBinaryOPCopmareF64(value1.(float64), float64(v2), bi.op)
			} else {
				return selfx.visitBinaryOPCopmareF64(value1.(float64), value2.(float64), bi.op)
			}
		}
		//break
	case "&&", "||":
		return selfx.visitBinaryOPLogic(value1.(bool), value2.(bool), bi.op)
		//break
	case "=":
		if visit1left != nil {
			selfx.setVariableValue(visit1left.variable.Name, value2)
		} else {
			fmt.Println("Assignment need a left value: ")
		}
	default:
		fmt.Println("Unsupported binary operation: " + bi.op)
	}

	return nil
}

func (selfx Intepretor) visitBinaryMath(v1 int, v2 int, op string) int {
	var ret int

	switch op {
	case "+":
		ret = v1 + v2
	case "-":
		ret = v1 - v2
	case "*":
		ret = v1 * v2
	case "/":
		ret = v1 / v2
	case "%":
		ret = v1 % v2
	default:
		fmt.Println("Unsupported binary operation: " + op)
	}
	return ret
}

func (selfx Intepretor) visitBinaryOPMathF64(v1 float64, v2 float64, op string) float64 {
	var ret float64

	switch op {
	case "+":
		ret = v1 + v2
	case "-":
		ret = v1 - v2
	case "*":
		ret = v1 * v2
	case "/":
		ret = v1 / v2
	default:
		fmt.Println("Unsupported binary operation: " + op)
	}
	return ret
}

func (selfx Intepretor) visitBinaryOPCopmareF64(v1 float64, v2 float64, op string) bool {
	var ret bool

	switch op {
	case ">":
		ret = v1 > v2
	case ">=":
		ret = v1 >= v2
	case "<":
		ret = v1 < v2
	case "<=":
		ret = v1 <= v2
	case "==":
		ret = v1 == v2
	default:
		fmt.Println("Unsupported binary operation: " + op)
	}

	return ret
}

func (selfx Intepretor) visitBinaryOPLogic(v1 bool, v2 bool, op string) bool {
	var ret bool

	switch op {
	case "&&":
		ret = v1 && v2
	case "||":
		ret = v1 || v2
	default:
		fmt.Println("Unsupported binary operation: " + op)
	}

	return ret
}

/**
 * 左值。
 * 目前先只是指变量。
 */
type LeftValue struct {
	variable *Variable
}

func NewLeftValue(variable Variable) *LeftValue {
	return &LeftValue{
		variable: &variable,
	}
}
